﻿using UnityEngine;
using System.Collections;
using System.Collections.Generic;
namespace Clojure
{
 
    public interface IFunction 
    {
        ASTNode Exec(List<ASTNode> args, Context context);
    }


    public static class CoreFunction
    {

        public static void SetCoreFunction(Context context)
        {
            context.Define("log", new FunctionNode(CoreFunction.Log));
            context.Define("+", new FunctionNode(CoreFunction.Add));
            context.Define("<", new FunctionNode(CoreFunction.Less));
            context.Define("do", new FunctionNode(CoreFunction._do));
            context.Define("if", new FunctionNode(CoreFunction.IF));
            context.Define("==",new FunctionNode(CoreFunction.Equal));
            context.Define("def",new FunctionNode(CoreFunction.Def));
            context.Define("/",new FunctionNode(CoreFunction.Except));
            context.Define("repeat",new FunctionNode(CoreFunction.Repeat));
            context.Define("vector-s",new FunctionNode(CoreFunction.Vector_s));
        }

        public static List<ASTNode> EvalArgs(List<ASTNode> args, Context context)
        {
            for (int i = 0; i < args.Count; i++)
            {
                args[i] = RT.Evaluate(args[i], context);
            }
            return args;
        }

        public static ASTNode Vector_s(List<ASTNode> args,Context context) 
        {
            args = EvalArgs(args,context);
            VectorNode retVecNode = new VectorNode();
            int size = (int)((NumberNode)args[0]).NumberVal;
            retVecNode.nodes = new List<ASTNode>(size);
            for (int i = 0; i < size;i++)
            {
                retVecNode.nodes.Add(args[1]);
            }
            return retVecNode;
        }
        public static ASTNode Repeat(List<ASTNode> args, Context context) 
        {
            return null;
        }

        public static ASTNode Equal(List<ASTNode> args, Context context)
        {
            BoolNode b = new BoolNode();
            b.Val = false;
            args = EvalArgs(args,context);
            if (args[0].NodeType == ASTNode.ASTNodeType.NumberNode && args[1].NodeType == ASTNode.ASTNodeType.NumberNode) 
            {
                NumberNode number1 = (NumberNode)args[0];
                NumberNode number2 = (NumberNode)args[1];
                if (number1.Type == NumberNode.NumberType.INT && number2.Type == NumberNode.NumberType.INT)
                {
                    if ((int)number1.NumberVal == (int)number2.NumberVal)
                    {
                        b.Val = true;
                    }
                    else 
                    {
                        b.Val = false;
                    }
                }
            }
            else if (args[0].NodeType == ASTNode.ASTNodeType.ObjectNode && args[1].NodeType == ASTNode.ASTNodeType.ObjectNode)
            {
                if (((ObjectNode)args[0]).Val == ((ObjectNode)args[1]).Val)
                {
                    b.Val = true;
                }
                else
                {
                    b.Val = false;
                }
            }
            else if(args[0].NodeType== ASTNode.ASTNodeType.BoolNode&&args[1].NodeType== ASTNode.ASTNodeType.BoolNode)
            {
                if (((BoolNode)args[0]).Val == ((BoolNode)args[1]).Val)
                {
                    b.Val = true;
                }
            }
            return b;
        }

        public static ASTNode Def(List<ASTNode> args, Context context)
        {
            string varName = ((SymbolNode)args[0]).Val;
            context.Define(varName, RT.Evaluate(args[1], context));
            return null;
        }
        public static ASTNode Log(List<ASTNode> args, Context context)
        {
            args[0] = RT.Evaluate(args[0], context);
            switch (args[0].NodeType)
            {
                case ASTNode.ASTNodeType.StringNode:
                    Debug.Log(((StringNode)args[0]).Val);
                    break;
                case ASTNode.ASTNodeType.NumberNode:
                    Debug.Log(((NumberNode)args[0]).NumberVal);
                    break;
                case ASTNode.ASTNodeType.BoolNode:
                    Debug.Log(((BoolNode)args[0]).Val);
                    break;
            }
            return null;
        }

        public static ASTNode Add(List<ASTNode> args, Context context) 
        {
            NumberNode retNode = new NumberNode();
            retNode.NumberVal = 0;
            while(args.Count>0)
            {
              NumberNode numNode = (NumberNode)RT.Evaluate(args[0],context);
              args.RemoveAt(0);
              switch (numNode.Type) 
              {
                  case NumberNode.NumberType.INT:
                      if (retNode.Type == NumberNode.NumberType.INT)
                      {
                          retNode.NumberVal = (int)retNode.NumberVal + (int)numNode.NumberVal;
                      }
                      else 
                      {
                          retNode.NumberVal = (float)retNode.NumberVal + (int)numNode.NumberVal;
                      }
                  break;
                  case NumberNode.NumberType.FLOAT:
                    retNode.Type = NumberNode.NumberType.FLOAT;
                    if (retNode.Type == NumberNode.NumberType.INT)
                    {
                        retNode.NumberVal = (float)retNode.NumberVal + (int)numNode.NumberVal;
                    }
                    else 
                    {
                        retNode.NumberVal = (float)retNode.NumberVal + (float)numNode.NumberVal;
                    }
                  break;
              }
            }
            return retNode;
        }

        public static ASTNode IF(List<ASTNode> args, Context context)
        {
            args[0] =RT.Evaluate(args[0], context);
            if (((BoolNode)args[0]).Val)
            {
                RT.Evaluate(args[1], context);
            }
            else
            {
                if (args.Count == 3) 
                {
                    RT.Evaluate(args[2],context);
                }
            }
            return null;
        }

        //<
        public static ASTNode Less(List<ASTNode> args, Context context) 
        {
            BoolNode boolNode = new BoolNode();
            NumberNode numberNode0 = (NumberNode)RT.Evaluate(args[0], context);
            NumberNode numberNode1 = (NumberNode)RT.Evaluate(args[1], context);

            float num0 = numberNode0.Type == NumberNode.NumberType.INT ?(float)(int)numberNode0.NumberVal:(int)numberNode0.NumberVal;
            float num1 = numberNode1.Type == NumberNode.NumberType.INT ? (float)(int)numberNode1.NumberVal : (int)numberNode1.NumberVal;
              
            if (num0 < num1)
            {
                boolNode.Val = true;
            }
            else 
            {
                boolNode.Val = false;   
            }
            return boolNode;
        }

        public static ASTNode _do(List<ASTNode> args, Context context)
        {
            for (int i = 0; i < args.Count;i++)
            {
                RT.Evaluate(args[i], context);
            }
            return null;
        }
         
        public static ASTNode Except(List<ASTNode> args,Context context) 
        {
            NumberNode numberNode0 = (NumberNode)RT.Evaluate(args[0], context);
            NumberNode numberNode1 = (NumberNode)RT.Evaluate(args[1], context);
            float num0 = numberNode0.Type == NumberNode.NumberType.INT ? (float)(int)numberNode0.NumberVal : (int)numberNode0.NumberVal;
            float num1 = numberNode1.Type == NumberNode.NumberType.INT ? (float)(int)numberNode1.NumberVal : (int)numberNode1.NumberVal;
            NumberNode retNumberNode =  new NumberNode();
            retNumberNode.NumberVal = num0/num1;
            retNumberNode.Type = NumberNode.NumberType.FLOAT;
            return retNumberNode; 
        }
    }
}
 